#include "MainScheduler.h"

/***************************************************************************************
* function name: MapTypeScheduler(const int key, TaskScheduler& mapValue)
* The Input: Map object's key and reference to task scheduler to be map object's value
* The output: None
* The Function operation: Sets new map object's attributes as given values
***************************************************************************************/
MainScheduler::MapTypeScheduler::MapTypeScheduler(const int key, TaskScheduler& mapValue): m_key(key), m_Value(mapValue) { }


/***************************************************************************************
* function name: ~MapTypeScheduler()
* The Input: None
* The output: None
* The Function operation: Deletes pointed task scheduler
***************************************************************************************/
MainScheduler::MapTypeScheduler::~MapTypeScheduler() {
	delete &m_Value; // delete pointed task scheduler
}

/***************************************************************************************
* function name: MapLinkedList()
* The Input: None
* The output: None
* The Function operation: Initializes an empty map object linked list using DataLinkedList
* 						  constructor
***************************************************************************************/
MainScheduler::MapLinkedList::MapLinkedList(): DataLinkedList() { }

/***************************************************************************************
* function name: ~MapLinkedList()
* The Input: None
* The output: None
* The Function operation: Deletes all nodes' pointed data
***************************************************************************************/
MainScheduler::MapLinkedList::~MapLinkedList() {
	// use an iterator to scan list and delete each scanned node's pointed data
	DataLinkedList::Iterator itr = begin();
	while (*itr != NULL) { // scan to list's end
		delete ((MapTypeScheduler*)*itr); // delete pointed data
		++itr; // move to next node
	}
}

/***************************************************************************************
* function name: void internalAdd(MapTypeScheduler& newData, const Iterator& positionHolder)
* The Input: Reference to map object to be added and reference to an iterator pointing to
* 		     the node that before it new object will be linked
* The output: None
* The Function operation: Uses DataLinkedList::internalAdd in order to add given object
* 						  at its position
***************************************************************************************/
void MainScheduler::MapLinkedList::internalAdd(MapTypeScheduler& newData, const Iterator& positionHolder) {
	DataLinkedList::internalAdd(newData, positionHolder); // add given object to list
}

/***************************************************************************************
* function name: TaskScheduler& createScheduler(const int key)
* The Input: Key in map that should hold new scheduler
* The output: Reference to the new scheduler
* The Function operation: Activates the constructor that matches given key and returns
* 						  a reference to the dynamically allocated object
***************************************************************************************/
TaskScheduler& MainScheduler::createScheduler(const int key) const {
	// create a scheduler matching given key
	TaskScheduler* schedToReturn = NULL; // initialize a pointer to returned scheduler
	switch(key) {
	case PRINTER_SPOOLER: { // create a new printer spooler
		 schedToReturn = new PrinterSpooler();
		break;
	}

	case DISK_DRIVER: { // create a new disk driver
		schedToReturn = new DiskDriver();
		break;
	}

	case CALC_DISPATCHER: { // create a new calculation dispatcher
		schedToReturn = new CalcDispathcer();
		break;
	}

	default: { // invalid value given
		break;
	}

	} // switch(key) {

	return *schedToReturn; // return reference to new scheduler
}

/***************************************************************************************
* function name: MainScheduler()
* The Input: None
* The output: None
* The Function operation: Initializes wall clock
***************************************************************************************/
MainScheduler::MainScheduler() {
	m_wallClock = INIT_WALL_CLOCK; // initialize wall clock with defined initialization value
}

/***************************************************************************************
* function name: ~MainScheduler()
* The Input: None
* The output: None
* The Function operation: Empty implementation since object has no dynamically allocated
* 					      attributes
***************************************************************************************/
MainScheduler::~MainScheduler() { }


/***************************************************************************************
* function name: void addJob(const unsigned int startTime, const unsigned int length, const int priority, const int type)
* The Input: New job's attributes and its type
* The output: None
* The Function operation: In map list, looks for node holding map object with a value
* 					      that is equal to given type.
* 					      If such if found, given values (besides type) are given to
* 					      the scheduler the map holds.
* 					      If not, a scheduler matching given type is created and a map
* 					      object holding it is added to map linked list. Than given
* 					      values (besides type) are given to the new scheduler
***************************************************************************************/
void MainScheduler::addJob(const unsigned int startTime, const unsigned int length, const int priority, const int type) {

	// initialize an iterator to map list start
	DataLinkedList::Iterator mapListItr = m_mapList.begin();
	++mapListItr; // move to first valid node

	// scan map list to its end
	while (*mapListItr != NULL) {

		if ( *((MapTypeScheduler*)(*mapListItr))>type ) { // no scheduler matching given type
			break; // stop scanning and add at current point

//			// create a new scheduler matching given type // TODO might be deleted
//			TaskScheduler& newScheduler = createScheduler(type);
//			// add new job to new scheduler
//			newScheduler.addJob(startTime, length, priority);
//			// create a new map object that holds new scheduler
//			MapTypeScheduler* newMap = new MapTypeScheduler(type, newScheduler);
//			// add new map object to map list
//			m_mapList.internalAdd(*newMap, mapListItr);
//			return;
		}

		if ( *((MapTypeScheduler*)(*mapListItr))==type ) { // compare current map object's key to given type
			// found match, add to current scheduler
			((MapTypeScheduler*)(*mapListItr))->getValue().addJob(startTime, length, priority);
			return;
		}
		++mapListItr; // move to next node on list
	}

	// create a new scheduler matching given type
	TaskScheduler& newScheduler = createScheduler(type);
	// add new job to new scheduler
	newScheduler.addJob(startTime, length, priority);
	// create a new map object that holds new scheduler
	MapTypeScheduler* newMap = new MapTypeScheduler(type, newScheduler);
	// add new map object to map list
	m_mapList.internalAdd(*newMap, mapListItr);
	return;
}

/***************************************************************************************
* function name: void execute()
* The Input: None
* The output: None
* The Function operation: Uses an iterator to scan map list and executes each scheduler
* 						  sequentially, from lowest job type to highest
***************************************************************************************/
void MainScheduler::execute() {

	// initialize an iterator to map list's start
	DataLinkedList::Iterator mapListItr = m_mapList.begin();
	++mapListItr; // move to first valid node

	if (m_mapList.isEmpty()) {
		return; // no jobs to run
	}

	unsigned int prevWallClock; // indicates if wall clock was changed during scan

	while (*mapListItr != NULL) { // scan map list to its end

		prevWallClock = m_wallClock; // backup wall clock

		if ( !((MapTypeScheduler*)(*mapListItr))->getValue().isEmpty() ) { // current scheduler is not empty
			if( ((MapTypeScheduler*)(*mapListItr))->getValue().work(m_wallClock, ((MapTypeScheduler*)(*mapListItr))->getKey() ) ) { // executed a job
				++m_wallClock; // increment system time
			}
		} else {
			m_mapList.removeNode(mapListItr); // current scheduler is empty, remove it
		}

		if (m_mapList.isEmpty()) { // map list is empty, no more jobs in system
			return;
		}

		++mapListItr; // move to next node

		if (*mapListItr == NULL) { // reached map list's end
			mapListItr = m_mapList.begin(); // initialize iterator to map list's start
			++mapListItr; // move to first valid node

			// if during scan system time was not changed- completed a scan without any execution
			if (prevWallClock == m_wallClock) {
				cout << "Time#" << m_wallClock << ": no job to run" << endl; // no available job message
				++m_wallClock; // increment system time
			}

		}

	} // while (*mapListItr != NULL)
}



//void MainScheduler::execute() {
//	// initialize an iterator to map list's start
//	DataLinkedList::Iterator mapListItr = m_mapList.begin();
//	++mapListItr; // move to first valid node
//
//	bool noJobExecuted = false;
//	bool currExecution = false;
//
//	// keep scanning map list until it is empty
//	while (!m_mapList.isEmpty()) {
//		noJobExecuted = false;
////		if (*mapListItr == NULL) { // reached map list's end
////			mapListItr = m_mapList.begin(); // initialize iterator to map list's start
////			++mapListItr; // move to first valid node
////			//++m_wallClock; // completed
////		}
//
//		if (*mapListItr != NULL) {
//
//			// check if current scheduler is not empty
//			if ( !((MapTypeScheduler*)(*mapListItr))->getValue().isEmpty() ){
//				// try executing its first job
//				currExecution = ((MapTypeScheduler*)(*mapListItr))->getValue().work(m_wallClock, ((MapTypeScheduler*)(*mapListItr))->getKey());
//				//if ( ((MapTypeScheduler*)(*mapListItr))->getValue().work(m_wallClock, ((MapTypeScheduler*)(*mapListItr))->getKey()) ){
//				if (currExecution) {
//					++m_wallClock; // if a job has been executed, increment time
//				} else {
//					noJobExecuted = noJobExecuted || currExecution;
//				}
//
//
//			} else { // current scheduler is empty, remove it from map list
//				m_mapList.removeNode(mapListItr);
//			}
//		} else {
//			mapListItr = m_mapList.begin(); // initialize iterator to map list's start
//			++mapListItr; // move to first valid node
//		}
//		if (noJobExecuted == false) { // no job was executed
//			++m_wallClock; // scanned entire map list, move to next node
//		}
//		++mapListItr; // move to next node
//	} // while (m_mapList.isEmpty())
//
//
//}
